# coding:utf-8

import time

import wx.lib.rcsizer as rcs
import matplotlib
import matplotlib.figure
import matplotlib.backends.backend_wxagg
import matplotlib.backends.backend_wx
from mpl_toolkits.axes_grid1.axes_divider import make_axes_area_auto_adjustable
from matplotlib import rcParams

rcParams['font.family'] = 'sans-serif'
rcParams['font.sans-serif'] = ['Tahoma']
rcParams['font.size'] = '8'

try:
    from agw import fourwaysplitter as FWS
except ImportError:  # if it's not there locally, try the wxPython lib.
    try:
        import wx.lib.agw.fourwaysplitter as FWS
    except ImportError:
        exit()

# 菜单ID
from AppIds import *

import wx.lib.newevent

# Frame start
SERIAL_GENIUS_FS_CHAR = 0x0F
# Slash
SERIAL_GENIUS_SL_CHAR = 0x1F


# 变量跟踪
class Variabletrack(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, style=wx.BORDER_DOUBLE)

        ############# 状态信息 ############
        # 该模块是否启用
        self.isUsing = False

        ############# 配置信息 ############
        self.dataBuffer = []
        # 已经存在的数据源字典
        # INT8U-0 : dataTrack index
        self.dataTrackDict = {}
        # 已经存在的数据源
        self.dataTrack = []
        # X时间轴
        self.dataT = []
        # 是否显示
        self.dataShow = []
        # 图形句柄
        self.dataAxes = []
        # 自动刷新
        self.autoScale = True
        # 数据是否已经改变
        self.dataChanged = False
        # 是否清除数据
        self.dataCleared = False

        self.dataAddingSignal = 1

        self.refreshTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.PlotData)
        self.refreshTimer.Start(500)

        # 命令解析状态
        # 0 无
        # 1 INT8U
        # 2 INT8S
        # 3 INT16U
        # 4 INT16S
        # 5 INT32U
        # 6 INT32S
        self.dataParseCurStatus = 0
        self.dataparseCurLength = 0

        ############# 开始布局 ###########
        gridSizer = rcs.RowColSizer()
        rowTotal = 4
        colTotal = 3

        # 标题
        title = wx.StaticText(self, -1, u"变量跟踪")
        title.SetFont(wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
        gridSizer.Add(title, pos=(0, 0), size=(1, colTotal), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)

        # 绘图区
        self.figure = matplotlib.figure.Figure(figsize=(1, 1))
        self.figure.subplots_adjust(left=0.00, bottom=0.00, right=1.00, top=1.00, wspace=None, hspace=None)
        self.axes = self.figure.add_subplot(111)
        self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg(self, 20, self.figure)
        self.canvas.mpl_connect('button_press_event', self.on_button_press_event)
        gridSizer.Add(self.canvas, pos=(1, 0), size=(1, colTotal), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
        make_axes_area_auto_adjustable(self.axes)
        self.axes.grid(True)

        # 清空
        btn = wx.Button(self, -1, u"清空", size=(40, 35))
        self.Bind(wx.EVT_BUTTON, self.OnClearData, btn)
        gridSizer.Add(btn, pos=(2, 0), size=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL)

        # 数据
        btn = wx.Button(self, -1, u"数据", size=(40, 35))
        self.Bind(wx.EVT_BUTTON, self.OnSelectData, btn)
        gridSizer.Add(btn, pos=(2, 1), size=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL)

        # 工具条
        self.toolbar = matplotlib.backends.backend_wx.NavigationToolbar2Wx(self.canvas)
        self.toolbar.Realize()
        # On Windows platform, default window size is incorrect, so set
        # toolbar width to figure width.
        # tw, th = self.toolbar.GetSizeTuple()
        # fw, fh = self.canvas.GetSizeTuple()
        # By adding toolbar in sizer, we are able to put it at the bottom
        # of the frame - so appearance is closer to GTK version.
        # As noted above, doesn't work for Mac.
        # self.toolbar.SetSize(wx.Size(fw, th))
        # self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
        # update the axes menu on the toolbar
        gridSizer.Add(self.toolbar, pos=(2, 2), size=(1, colTotal - 2), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
        # self.toolbar.update()

        # 设置变宽信息
        gridSizer.AddGrowableCol(2)
        gridSizer.AddGrowableRow(1)

        ############# 结束布局 ###########
        self.SetSizer(gridSizer)
        self.SetAutoLayout(True)

    # 电机鼠标事件
    def on_button_press_event(self, event):
        if 'PAN' == self.axes.get_navigate_mode() or 'ZOOM' == self.axes.get_navigate_mode():
            self.autoScale = False
        else:
            self.autoScale = True

    # 添加一个数据到数据跟踪
    def AddData(self, cmd, index, data):
        if cmd == 0x07:
            cmdStr = 'INT8U-' + str(index)
        elif cmd == 0x08:
            cmdStr = 'INT8S-' + str(index)
        elif cmd == 0x09:
            cmdStr = 'INT16U-' + str(index)
        elif cmd == 0x0A:
            cmdStr = 'INT16S-' + str(index)
        elif cmd == 0x0B:
            cmdStr = 'INT32U-' + str(index)
        elif cmd == 0x0C:
            cmdStr = 'INT32S-' + str(index)
        else:
            return

        self.dataAddingSignal -= 1
        while self.dataAddingSignal < 0:
            time.sleep(0.01)

        if cmdStr in self.dataTrackDict:
            index = self.dataTrackDict.get(cmdStr)
        else:
            self.dataShow.append(True)
            self.dataT.append([])
            self.dataTrack.append([])
            self.dataAxes.append(None)

            index = len(self.dataShow) - 1
            self.dataTrackDict[cmdStr] = index
            self.dataAxes[index], = self.axes.plot([], [])  # , label=cmdStr)

        self.dataTrack[index].append(data)
        self.dataT[index].append(len(self.dataT[index]))
        self.dataAddingSignal += 1
        self.dataChanged = True

    # 绘制选定的图形到界面
    def PlotData(self, evt=None):
        if not self.dataChanged:
            return

        self.dataAddingSignal -= 1
        if self.dataAddingSignal < 0:
            self.dataAddingSignal += 1
            return

        self.dataChanged = False

        if self.dataCleared:
            self.axes.clear()
            self.dataCleared = False
            self.dataTrackDict.clear()
            del self.dataTrack[:]
            del self.dataT[:]
            del self.dataShow[:]
            del self.dataAxes[:]

        tMax = 1
        yMin = 0
        yMax = 1
        hasData = False
        legends = []

        for key, index in self.dataTrackDict.items():
            if self.dataShow[index]:
                hasData = True
                if self.dataAxes[index]:
                    if len(self.dataT[index]) != len(self.dataTrack[index]):
                        continue

                    self.dataAxes[index].set_data(self.dataT[index], self.dataTrack[index])
                    self.dataAxes[index].set_label(key)
                    legends.append(key)

                    if self.autoScale and len(self.dataT[index]) > 0:
                        tlen1 = len(self.dataT[index])
                        if tMax < tlen1:
                            tMax = tlen1

                        ymin1 = min(self.dataTrack[index])
                        if yMin > ymin1:
                            yMin = ymin1

                        ymax1 = max(self.dataTrack[index])
                        if yMax < ymax1:
                            yMax = ymax1
            else:

                self.dataAxes[index].set_data([], [])
                self.dataAxes[index].set_label('_nolegend_')

        if self.autoScale:
            self.axes.set_xlim(0, tMax + 1)
            self.axes.set_ylim(yMin - 1, yMax + 1)

        if hasData:
            self.axes.legend(legends)
        else:
            self.axes.legend(('None',))

        try:
            self.canvas.draw()
        except:
            pass

        self.dataAddingSignal += 1

    # 获取数据部分中下一个数据（如果转义自动去除转义符号）
    def GetNextDataByte(self):
        if self.dataBuffer[0] == SERIAL_GENIUS_SL_CHAR:
            self.dataBuffer.pop(0)
        return self.dataBuffer.pop(0)

    # 新增串口数据，被串口接收所调用
    def RecvData(self, data):
        self.dataBuffer.extend(data)
        '''
        f=open('a.txt','a')
        for i in range(0,len(data)):
            f.write('%02X ' % data[i])
        f.close()
        '''
        # 解析数据
        while (self.dataParseCurStatus == 0 and len(self.dataBuffer) >= 5) or (
                        self.dataParseCurStatus != 0 and len(self.dataBuffer) >= self.dataparseCurLength):
            try:
                if self.dataParseCurStatus == 0:
                    # 找到帧头
                    while len(self.dataBuffer) > 1:
                        if self.dataBuffer[0] == SERIAL_GENIUS_FS_CHAR:
                            break
                        if self.dataBuffer[0] == SERIAL_GENIUS_SL_CHAR:
                            self.dataBuffer.pop(0)
                        self.dataBuffer.pop(0)

                    if len(self.dataBuffer) < 5:
                        break

                    # 0F 命令 长度 校验 数据...
                    # 无
                    if self.dataBuffer[0] == SERIAL_GENIUS_FS_CHAR:
                        if 0x07 <= self.dataBuffer[1] <= 0x0C:
                            if self.dataBuffer[3] == self.dataBuffer[1] ^ self.dataBuffer[2]:
                                self.dataParseCurStatus = self.dataBuffer[1]
                                self.dataparseCurLength = self.dataBuffer[2]

                                self.dataBuffer.pop(0)
                                self.dataBuffer.pop(0)
                                self.dataBuffer.pop(0)
                                self.dataBuffer.pop(0)
                            else:
                                # 数据校验出错
                                self.dataBuffer.pop(0)
                                self.dataBuffer.pop(0)
                                self.dataBuffer.pop(0)
                                self.dataBuffer.pop(0)
                        else:
                            # 不是我们的数据
                            self.dataBuffer.pop(0)
                            self.dataBuffer.pop(0)
                            self.dataBuffer.pop(0)
                            self.dataBuffer.pop(0)
                    else:
                        # 不是一个帧头，在这里忽略
                        self.dataBuffer.pop(0)
                elif self.dataParseCurStatus == 0x07:
                    self.dataParseCurStatus = 0

                    item = self.GetNextDataByte()
                    value = self.GetNextDataByte()

                    if 0 <= item <= 255:
                        self.AddData(0x07, item, value)
                elif self.dataParseCurStatus == 0x08:
                    self.dataParseCurStatus = 0

                    # INT8S
                    item = self.GetNextDataByte()
                    value = self.GetNextDataByte()
                    if value > 127:
                        value = value - 0x100
                    if 0 <= item <= 255:
                        self.AddData(0x08, item, value)
                elif self.dataParseCurStatus == 0x09:
                    self.dataParseCurStatus = 0

                    # INT16U
                    item = self.GetNextDataByte()
                    valueH = self.GetNextDataByte()
                    valueL = self.GetNextDataByte()
                    value = (valueH << 8) | valueL

                    if 0 <= item <= 255:
                        self.AddData(0x09, item, value)
                elif self.dataParseCurStatus == 0x0A:
                    self.dataParseCurStatus = 0

                    # INT16S
                    item = self.GetNextDataByte()
                    valueH = self.GetNextDataByte()
                    valueL = self.GetNextDataByte()
                    value = (valueH << 8) | valueL
                    if value > 0x7FFF:
                        value = value - 0x10000

                    if 0 <= item <= 255:
                        self.AddData(0x0A, item, value)
                elif self.dataParseCurStatus == 0x0B:
                    self.dataParseCurStatus = 0

                    # INT32U
                    item = self.GetNextDataByte()
                    valueHH = self.GetNextDataByte()
                    valueHL = self.GetNextDataByte()
                    valueLH = self.GetNextDataByte()
                    valueLL = self.GetNextDataByte()
                    value = (valueHH << 24) | (valueHL << 16) | (valueLH << 8) | valueLL

                    if 0 <= item <= 255:
                        self.AddData(0x0B, item, value)
                elif self.dataParseCurStatus == 0x0C:
                    self.dataParseCurStatus = 0

                    # INT32S
                    item = self.GetNextDataByte()
                    valueHH = self.GetNextDataByte()
                    valueHL = self.GetNextDataByte()
                    valueLH = self.GetNextDataByte()
                    valueLL = self.GetNextDataByte()
                    value = (valueHH << 24) | (valueHL << 16) | (valueLH << 8) | valueLL
                    if value > 0x7FFFFFFF:
                        value = value - 0x100000000

                    if 0 <= item <= 255:
                        self.AddData(0x0C, item, value)
                else:
                    self.dataParseCurStatus = 0
            except:
                self.dataParseCurStatus = 0
                break

    # 清空数据
    def OnClearData(self, event):
        self.dataCleared = True
        self.dataChanged = True

    # 选择数据
    def OnSelectData(self, event):
        # 调试用作输出数据使用
        '''
        print "\n=======================self.dataTrackDict========================\n"
        print self.dataTrackDict
        print "\n=======================self.dataTrack========================\n"
        print self.dataTrack
        print "\n=======================self.dataT========================\n"
        print self.dataT
        print "\n=======================self.dataShow========================\n"
        print self.dataShow
        print "\n=======================self.dataAxes========================\n"
        print self.dataAxes     
        return 
        '''
        lst = []
        lstSel = []
        for key, index in self.dataTrackDict.items():
            lst.append(key)
            if self.dataShow[index]:
                lstSel.append(index)

        dlg = wx.MultiChoiceDialog(self, u"选择需要显示的数据", u"已接收到的数据跟踪源", lst)
        dlg.SetSelections(lstSel)

        if (dlg.ShowModal() == wx.ID_OK):
            selections = dlg.GetSelections()
            for index in range(0, len(self.dataShow)):
                self.dataShow[index] = False
            for index in selections:
                self.dataShow[index] = True
            self.dataChanged = True

        dlg.Destroy()
        # 重新绘图
        self.PlotData()

    # 重绘函数
    def OnPaint(self, event):
        self.canvas.draw()
